package com.mspbots.sync.wise;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.api.RestMessageObject;
import com.mspbots.sync.BaseGenericService;
import com.mspbots.sync.model.ExceptionLog;
import com.mspbots.sync.wise.client.ClientProperties;
import com.mspbots.sync.wise.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

/**
 * com.mspbots.sync.scheduled.connectwise.ConnectWiseSyncService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/7/30
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class WiseSyncService extends BaseGenericService {

    private final WiseUserService wiseUserSyncService;
    private final TimeEntriesService timeEntriesSyncService;
    private final TicketEntryService ticketEntryService;
    private final BoardService boardService;
    private final ProjectPhaseService phaseService;
    private final CompanyService companyService;
    private final ScheduleService scheduleEntryService;
    private final SkillsService skillsService;
    private final SlasService slasService;
    private final SubdataService subdataService;

    public Mono<Object> connectReady(Integer tenantId) {
        return this.onGetWiseProperties(tenantId).flatMap(this::existsConnect);
    }

    public Mono<Object> existsConnect(ClientProperties configuration) {
        return cwWebClient.getRepository("/system/info", null)
                .subscriberContext(ctx -> ctx.put("properties", configuration))
                .map(jsonNode -> !StringUtils.isEmpty(jsonNode.get("version")));
    }

    public void manualSyncTickets(Integer tenantId) {
        this.onGetWiseProperties(tenantId)
                .subscribe(this.ticketEntryService::manualSyncTickets,
                        err -> log.error("This tenant {} configuration is empty.", tenantId));
    }

    public Mono<Object> manualSync(Integer tenantId) {
        return this.onGetWiseProperties(tenantId)
                .doOnNext(wiseClientProperties -> {
                    log.debug("Manual sync start tenant {}", wiseClientProperties.getTenantId());
                    this.autoSyncUsers(wiseClientProperties);
                    this.autoSyncScheduleEntries(wiseClientProperties);
                    this.autoSyncSubdata(wiseClientProperties);
                    this.autoSyncTimeEntries(wiseClientProperties);
                    this.boardService.autoSync(wiseClientProperties);
                    this.autoSyncContacts(wiseClientProperties);
                    this.autoSyncSkills(wiseClientProperties);
                    this.autoSyncCompanies(wiseClientProperties);
                    this.autoSyncAgreements(wiseClientProperties);
                    this.autoSyncProjectPhase(wiseClientProperties);
                })
                .map(configuration -> RestMessageObject.ok("Manual sync connect wise success"));
    }


    public void autoSyncUsers(ClientProperties configuration) {
        this.wiseUserSyncService.getUsers(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-USER").msg("Connect wise sync wise user success.")
                        .build()))
                .subscribeOn(Schedulers.elastic())
                .subscribe(wiseUser -> {
                    log.debug("Sync wise user save success. {}", wiseUser.getCwUid());
                    this.messagingTemplate.convertAndSend("msp.wise.user", "wise-users-callback", wiseUser);
                }, error -> log.error("Connect wise SYNC-WISE-USER error,msg:{}", error.getMessage()));
    }

    public void autoSyncCompanies(ClientProperties configuration) {
        this.companyService.getCompanies(configuration)
                .subscribeOn(Schedulers.elastic())
                .subscribe(companies -> {
                    log.debug("Sync wise Companies save success. {}", companies.get("id"));
                    ObjectNode entryNode = companies.deepCopy();
                    entryNode.put("tenantId", configuration.getTenantId());
                    this.messagingTemplate.convertAndSend("wise.companies", entryNode);
                }, error -> log.error("Connect wise SYNC-WISE-COMPANIES error,msg:{}", error.getMessage()));
    }

    public void autoSyncSlas(ClientProperties configuration) {
        this.slasService.getSlas(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-SLAS").msg("Connect wise sync SLAS entry success.")
                        .build()))
                .subscribeOn(Schedulers.elastic())
                .subscribe(slas -> {
                    log.debug("Sync wise SLAS save success. {}", slas.get("id"));
                    ObjectNode entryNode = slas.deepCopy();
                    entryNode.put("tenantId", configuration.getTenantId());
                    this.messagingTemplate.convertAndSend("wise.slas", entryNode);
                }, error -> log.error("Connect wise SYNC-WISE-SLAS error,msg:{}", error.getMessage()));
    }

    public void autoSyncSkills(ClientProperties configuration) {
        this.skillsService.getSkills(configuration)
                .subscribeOn(Schedulers.elastic())
                .subscribe(skills -> {
                    log.debug("Sync wise skills save success. {}", skills.get("id"));
                    ObjectNode entryNode = skills.deepCopy();
                    entryNode.put("tenantId", configuration.getTenantId());
                    this.messagingTemplate.convertAndSend("wise.skills", entryNode);
                }, error -> log.error("Connect wise SYNC-WISE-SKILLS error,msg:{}", error.getMessage()));
    }

    public void autoSyncProjectPhase(ClientProperties configuration) {
        this.phaseService.getProjectPhases(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-PROJECTPHASE").msg("Connect wise sync PHASE success.")
                        .build()))
                .subscribeOn(Schedulers.elastic())
                .subscribe(projectPhaseOnly -> {
                    log.debug("Sync wise phase save success. id: {}", projectPhaseOnly.getCwId());
                    this.messagingTemplate.convertAndSend("wise.project.phase", projectPhaseOnly);
                }, error -> log.error("Connect wise SYNC-WISE-PROJECTPHASE error,msg:{}", error.getMessage()));
    }

    public void autoSyncAgreements(ClientProperties configuration) {
        this.companyService.getAgreements(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-AGREEMENTS").msg("Connect wise sync AGREEMENTS entry success.")
                        .build()))
                .subscribeOn(Schedulers.elastic())
                .subscribe(agreementsOnly -> {
                    log.debug("Sync wise agreementsOnly save success. id: {}", agreementsOnly.get("id"));
                    ObjectNode entryNode = agreementsOnly.deepCopy();
                    entryNode.put("tenantId", configuration.getTenantId());
                    this.messagingTemplate.convertAndSend("wise.agreements", entryNode);
                }, error -> log.error("Connect wise SYNC-WISE-AGREEMENTS error,msg:{}", error.getMessage()));
    }

    public void autoSyncContacts(ClientProperties configuration) {
        this.companyService.getContacts(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-CONTACTS").msg("Connect wise sync CONTACTS entry success.")
                        .build())
                ).subscribeOn(Schedulers.elastic())
                .subscribe(contactsOnly -> {
                    log.debug("Sync wise contacts save success. id: {}", contactsOnly.get("id"));
                    ObjectNode entryNode = contactsOnly.deepCopy();
                    entryNode.put("tenantId", configuration.getTenantId());
                    this.messagingTemplate.convertAndSend("wise.contacts", entryNode);
                }, error -> log.error("Connect wise SYNC-WISE-CONTACTS error,msg:{}", error.getMessage()));
    }


    public void autoSyncTimeEntries(ClientProperties configuration) {
        this.timeEntriesSyncService.getEntries(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-TIMES").msg("Connect wise sync times entry success. " + configuration.getTenantId())
                        .build())
                )
                .subscribeOn(Schedulers.elastic())
                .subscribe(timeEntries -> {
                    log.debug("This tenant {} sync time entries data success,id: {}::timeentry:{}", configuration.getTenantId(),
                            timeEntries.getCwTid(), this.objectMapper.convertValue(timeEntries, JsonNode.class));
                    this.messagingTemplate.convertAndSend("msp.wise.entries", "", timeEntries);
                }, error -> log.error("Connect wise SYNC-WISE-TIMES,msg:{}", error.getMessage()));
    }

    public void autoSyncTicketEntries(ClientProperties configuration) {
        this.ticketEntryService.getEntries(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-TICKET").msg("Connect wise sync ticket entry success.")
                        .build()))
                .subscribeOn(Schedulers.elastic())
                .subscribe(entry -> {
                    log.info("This tenant {} sync ticket entries data success,id: {}",
                            entry.getTenantId(), entry.getCwId());
                    this.messagingTemplate.convertAndSend("msp.wise.callback.tickets", "", entry);
                }, error -> log.error("Connect wise SYNC-WISE-TICKETS error,msg:{}", error.getMessage()));
    }

    public void autoSyncScheduleEntries(ClientProperties configuration) {
        this.scheduleEntryService.getEntries(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-SCHEDULE").msg("Connect wise sync Schedule entry success.")
                        .build())
                )
                .subscribeOn(Schedulers.elastic())
                .subscribe(entry -> {
                    log.debug("This tenant {} sync schedules entries data success,id: {}",
                            configuration.getTenantId(), entry.get("id"));
                    ObjectNode entryNode = entry.deepCopy();
                    entryNode.put("tenantId", configuration.getTenantId());
                    this.messagingTemplate.convertAndSend("msp.wise.callback.schedules", "", entryNode);
                }, error -> log.error("Connect wise SYNC-WISE-SCHEDULES error,msg:{}", error.getMessage()));
    }


    public void autoSyncSubdata(ClientProperties configuration) {

        this.subdataService.getScheduleStatuses(configuration)
                .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(configuration.getTenantId()).name("Connect wise")
                        .status(200).type("SYNC-WISE-SCHEDULE-STATUSES")
                        .msg("Connect wise sync Schedule Statuses success.")
                        .build()))
                .mergeWith(this.subdataService.getCompanyTeamRoles(configuration)
                        .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                                .tenantId(configuration.getTenantId()).name("Connect wise")
                                .status(200).type("SYNC-WISE-COMPANY-TEAM-ROLES")
                                .msg("Connect wise sync COMPANY-TEAM-ROLES success.")
                                .build())))
                .mergeWith(this.subdataService.getScheduleCalendars(configuration)
                        .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                                .tenantId(configuration.getTenantId()).name("Connect wise")
                                .status(200).type("SYNC-WISE-SCHEDULE-CALENDARS")
                                .msg("Connect wise sync SYNC-WISE-SCHEDULE-CALENDARS success.")
                                .build())))
                .mergeWith(this.subdataService.getTimeZoneSetups(configuration)
                        .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                                .tenantId(configuration.getTenantId()).name("Connect wise")
                                .status(200).type("SYNC-WISE-TIME-ZONE-SETUPS")
                                .msg("Connect wise sync SYNC-WISE-TIME-ZONE-SETUPS success.")
                                .build())))
                .mergeWith(this.subdataService.getCompanyStatus(configuration)
                        .doFirst(() -> this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                                .tenantId(configuration.getTenantId()).name("Connect wise")
                                .status(200).type("SYNC-COMPANIES-STATUSES")
                                .msg("Connect wise SYNC-COMPANIES-STATUSES success.")
                                .build())))
                .subscribeOn(Schedulers.elastic())
                .subscribe(statusNode -> {
                    log.debug("Sync wise sub data save success. {}", statusNode);
                    this.messagingTemplate.convertAndSend("wise.subdata", statusNode);
                }, error -> log.error("Connect wise sub data error,msg:{}",
                        error.getMessage()));
    }
}
